package com.xiaoq.sites.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoq.ESIndexNameProperties;
import com.xiaoq.coms.util.ConstantUtil;
import com.xiaoq.sites.entity.*;
import com.xiaoq.sites.repository.*;
import com.xiaoq.sites.service.ISitesService;
import com.xiaoq.sites.vo.*;
import com.xiaoq.vo.PagingVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.highlight.HighlightField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
public class SitesServiceImpl implements ISitesService {

	@Autowired
	private ISitesRepository sitesRepo;

	@Autowired
	private ISitesPointRepository sitesPointRepo;

	@Autowired
	private ISitesAnnouncementRepository sitesAnnouncementRepo;

	@Autowired
	private ISitesRouteRepository sitesRouteRepo;

	@Autowired
	private ISitesRoutePointRepository sitesRoutePointRepo;

	@Autowired
	private ISitesESRepository sitesESRepository;

	@Autowired
	private ISitesHotSearchRepository sitesHotSearchRepository;

	@Autowired
	private Client client;

	@Autowired
	private ElasticsearchTemplate template;

	@Autowired
	private ESIndexNameProperties indexNameProperties;

	private static final ObjectMapper MAPPER = new ObjectMapper();

	private Logger logger = LoggerFactory.getLogger(SitesServiceImpl.class);

	@Override
	public SitesEntity findById(String id) {
		if(StringUtils.isNotBlank(id)) {
			return sitesRepo.findById(id);
		}
		return null;
	}

	/**
	 * 根据景区ID查找该景区的所有景点
	 */
	@Override
	public List<SitesPointEntity> findSitesPointListBySitesId(String sitesId) {
		List<SitesPointEntity> sitesPointList = null;
		if(StringUtils.isNotBlank(sitesId)){
			sitesPointList = sitesPointRepo.findBySitesId(sitesId);
		}
		return sitesPointList;
	}

	/**
	 * 根据景区ID查找该景区的所有公告
	 */
	@Override
	public List<SitesAnnouncementEntity> findSitesAnnouncementListBySitesId(String sitesId) {
		List<SitesAnnouncementEntity> sitesAnnouncementList = null;
		if(StringUtils.isNotBlank(sitesId)){
			sitesAnnouncementList = sitesAnnouncementRepo.findBySitesId(sitesId);
		}
		return sitesAnnouncementList;
	}

	/**
	 * 根据线路ID,查找该线路经过的所有景点
	 */
	@Override
	public List<SitesPointEntity> findSitesPointListByRouteId(String routeId) {
		List<SitesPointEntity> sitesPointList = null;
		List<SitesPointEntity> returnSitesPointList = null;
		if(StringUtils.isNotBlank(routeId)){
			List<SitesRoutePointEntity> sitesRoutePointList = sitesRoutePointRepo.findSitesRoutePointByRouteId(routeId);
			List<String> sitesPointIdList = null;
			if(CollectionUtils.isNotEmpty(sitesRoutePointList)) {
				sitesPointIdList = sitesRoutePointList.stream().map(sitesRoutePoint -> sitesRoutePoint.getSitesPointId()).collect(Collectors.toList());
				sitesPointList = sitesPointRepo.findBySitesPointIdList(sitesPointIdList);
			}

			//按升序排序
			if(CollectionUtils.isNotEmpty(sitesPointList)) {
				Map<String, SitesPointEntity> sitePointId2SitesPointMap = sitesPointList.stream()
						.collect(Collectors.toMap(SitesPointEntity::getId, SitesPointEntity -> SitesPointEntity));

				returnSitesPointList = new ArrayList<>();

				for (SitesRoutePointEntity sitesRoutePointEntity : sitesRoutePointList) {
					SitesPointEntity sitesPointEntity = sitePointId2SitesPointMap.get(sitesRoutePointEntity.getSitesPointId());
					if(sitesPointEntity != null){
						sitesPointEntity.setSortNum(sitesRoutePointEntity.getSortNum());
						returnSitesPointList.add(sitesPointEntity);
					}
				}
			}
		}
		return returnSitesPointList;
	}

	@Override
	public List<SitesRouteEntity> findSitesRouteListBySitesId(String sitesId) {
		List<SitesRouteEntity> sitesRouteList = null;
		if(StringUtils.isNotBlank(sitesId)){
			sitesRouteList = sitesRouteRepo.findSitesRouteListBySitesId(sitesId);
		}
		return sitesRouteList;
	}

	/**
	 * 根据线路ID List,查找该线路经过的所有景点
	 * @param routeIdList  线路ID List
	 * @return Map<String,List<SitesPointEntity>>  key:routeId,value为routeId对应的景点
	 */
	@Override
	public Map<String, List<SitesPointEntity>> findSitesPointMapByRouteIdList(List<String> routeIdList) {
		List<SitesRoutePointEntity> sitesRoutePointList = null;
		Map<String, List<SitesPointEntity>> resultMap = new HashMap<>();
		if(CollectionUtils.isNotEmpty(routeIdList)) {
			sitesRoutePointList = sitesRoutePointRepo.findSitesRoutePointByRouteIdList(routeIdList);
		}

		List<SitesPointEntity> sitesPointEntitieList = null;

		//<routeId, <pointId, SitesRoutePointEntity>> 有序
		Map<String, LinkedHashMap<String, SitesRoutePointEntity>>  routeId2LinkedMap = new HashMap<>();
		if(CollectionUtils.isNotEmpty(sitesRoutePointList)) {

			//得到所有的景点Id List
			List<String> sitesPointIdStrList = sitesRoutePointList.stream().map(sitesRoutePoint -> sitesRoutePoint.getSitesPointId()).collect(Collectors.toList());
			sitesPointEntitieList = sitesPointRepo.findBySitesPointIdList(sitesPointIdStrList);

			for (SitesRoutePointEntity sitesRoutePointEntity : sitesRoutePointList) {

				String routeId = sitesRoutePointEntity.getRouteId();
				String pointId = sitesRoutePointEntity.getSitesPointId();

				if(routeId2LinkedMap.get(routeId) != null && StringUtils.isNotBlank(pointId)) {
					LinkedHashMap<String, SitesRoutePointEntity> linkedHashMap = routeId2LinkedMap.get(routeId);
					linkedHashMap.put(pointId, sitesRoutePointEntity);
				}else if(StringUtils.isNotBlank(routeId) && StringUtils.isNotBlank(pointId)){
					LinkedHashMap<String, SitesRoutePointEntity> linkedHashMap = new LinkedHashMap<>();
					linkedHashMap.put(pointId, sitesRoutePointEntity);
					routeId2LinkedMap.put(routeId, linkedHashMap);
				}
			}
		}

		if(CollectionUtils.isNotEmpty(sitesPointEntitieList)) {
			//<pointId, SitesPointEntity>
			Map<String, SitesPointEntity> sitePointId2SitesPointMap = sitesPointEntitieList.stream()
					.collect(Collectors.toMap(SitesPointEntity::getId, sitesPointEntity -> sitesPointEntity));

			//<routeId, <pointId, SitesRoutePointEntity>> 有序
			routeId2LinkedMap.forEach((k,v) -> {
				List<SitesPointEntity> tmpSitesPointList = new ArrayList<>(); //有序

				v.forEach((pointId,sitesRoutePointEntity) -> {
					SitesPointEntity sitesPointEntity = sitePointId2SitesPointMap.get(pointId);
					SitesPointEntity tmpSitesPoint =  new SitesPointEntity();
					BeanUtils.copyProperties(sitesPointEntity, tmpSitesPoint);
					tmpSitesPoint.setSortNum(sitesRoutePointEntity.getSortNum());
					tmpSitesPointList.add(tmpSitesPoint);
				});

				resultMap.put(k, tmpSitesPointList);
			});
		}

		return resultMap;
	}

	@Override
	public List<SitesEntity> findSitesFromESByCityCodeAndKeyWord(String zoneCity, String searchText,int page,int size) {
		if(page < 0){
			page = 0;
		}
		if(size < 0){
			size = 10;
		}

		PageRequest pageRequest = new PageRequest(page, size);

		BoolQueryBuilder queryBuilder =  QueryBuilders.boolQuery().must(QueryBuilders.termQuery("zoneCity", zoneCity))
				.must(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", searchText)
						.fuzziness(Fuzziness.AUTO).prefixLength(1).maxExpansions(10).boost(0.98f))
						.should(QueryBuilders.wildcardQuery("shortName", "*"+searchText+"*").boost(0.8f))
						.should(QueryBuilders.wildcardQuery("introduction", "*"+searchText+"*").boost(0.02f)))
						.must(QueryBuilders.termQuery("delFlag", ConstantUtil.NOT_DELETE_STATUS));

		// 创建搜索 DSL 查询
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withPageable(pageRequest)
				.withQuery(queryBuilder).build();

		Page<SitesEntity> searchPageResults = sitesESRepository.search(searchQuery);
		if(searchPageResults!=null && CollectionUtils.isNotEmpty(searchPageResults.getContent())){
			return searchPageResults.getContent();
		}
		return new ArrayList<>();
	}

	@Override
	public boolean addSitesPoint(SitesPointEntity sitesPointEntity) {
		return sitesPointRepo.addSitesPoint(sitesPointEntity);
	}

	@Override
	public boolean addSites(SitesEntity sitesEntity) {
		return sitesRepo.addSites(sitesEntity);
	}

	@Override
	public boolean delSitesPoint(String pointId) {
		if(StringUtils.isNotBlank(pointId)){
			sitesPointRepo.delSitesPoints(pointId);
			sitesRoutePointRepo.delPointRoute(pointId);
			return true;
		}

		return false;
	}

	@Override
	public boolean updateSitesPoint(SitesPointEntity sitesPointEntity) {

		return sitesPointRepo.updateSitesPoint(sitesPointEntity);
	}

	@Override
	public boolean updateSites(SitesEntity sitesEntity) {

		return sitesRepo.updateSites(sitesEntity);
	}

	@Override
	public boolean delSites(String sitesId) {
		return sitesRepo.delSites(sitesId);
	}

	@Override
	public boolean delSites2(String sitesId) {
		return sitesRepo.delSites2(sitesId);
	}

	@Override
	public List<SitesEntity> findSitesFromESBySearchTxt(String searchTxt) {
		//暂时取前10条
		PageRequest pageRequest = new PageRequest(0, 10);

		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", searchTxt)
				.fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10).boost(0.98f))
				.should(QueryBuilders.wildcardQuery("name", "*"+searchTxt+"*").boost(0.8f))
				.must(QueryBuilders.termQuery("delFlag", ConstantUtil.NOT_DELETE_STATUS));

		// 创建搜索 DSL 查询
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withPageable(pageRequest)
				.withQuery(queryBuilder).build();

		Page<SitesEntity> searchPageResults = sitesESRepository.search(searchQuery);
		return searchPageResults.getContent();
	}

	@Override
	public void addSitesHotSearch(SitesHotSearchEntity sitesHotSearch) {
		if(sitesHotSearch != null) {
			sitesHotSearchRepository.addSitesHotSearch(sitesHotSearch);
		}
	}

	@Override
	public List<SitesHotSearchEntity> getSitesHotSearchList() {
		return sitesHotSearchRepository.getSitesHotSearchList();
	}

	@Override
	public List<SitesEntity> findSitesListbyZoneCity(String zoneCity,String zoneDistrict) {
		List<SitesEntity> findSitesList = null;
		//TODO  从redis里查
		if(StringUtils.isNotBlank(zoneCity)) {
			findSitesList = sitesRepo.findSitesList(zoneCity,zoneDistrict);
		}
		return findSitesList;
	}

	@Override
	public boolean updateAuthenStatus(String sitesId, String authenStatus) {
		return sitesRepo.updateAuthenStatus(sitesId, authenStatus);
	}

	@Override
	public List<ContentSearchVo> findContentSearchFromEsByTitle(String title, PagingVo pagingVo){
		List<ContentSearchVo> resultVos = new ArrayList<>();
		if(StringUtils.isBlank(title)){
			return resultVos;
		}

		Pageable pageable = new PageRequest(pagingVo.getPn(), pagingVo.getPs());
		QueryStringQueryBuilder builder = new QueryStringQueryBuilder(title).defaultField("title").analyzer("ik_smart");
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(builder)
				.withIndices(indexNameProperties.getSitesIndexName())
				.withTypes("content")
				.withHighlightFields(new HighlightBuilder.Field("title").fragmentSize(500).numOfFragments(20).preTags("<font color = red>").postTags("</font>"))
				.withPageable(pageable)
				.build();

		template.queryForPage(searchQuery, ContentSearchVo.class, new SearchResultMapper() {
			@Override
			public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
				SearchHit[] searchHits = response.getHits().getHits();

				if(searchHits==null || searchHits.length<1){
					return null;
				}

				for (SearchHit searchHit : response.getHits()) {
					String source = searchHit.getSourceAsString();
					ContentSearchVo contentSearchVo = null;
					try {
						contentSearchVo = MAPPER.readValue(source, ContentSearchVo.class);
						contentSearchVo.setId(searchHit.getId());
						contentSearchVo.setHtmlTitle(searchHit.getHighlightFields().get("title").fragments()[0].toString());
						resultVos.add(contentSearchVo);
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}
				}
				return null;
			}
		});

		return resultVos;
	}


	@Override
	public List<SearchResultVo> findContentFromEsByTitle(String title, PagingVo pagingVo) {
		List<SearchResultVo> resultVos = new ArrayList<>();
		List<ContentSearchVo> contentSearchList = this.findContentSearchFromEsByTitle(title, pagingVo);
		if(CollectionUtils.isEmpty(contentSearchList)){
			return resultVos;
		}

		SearchResultVo searchResultVo;
		for (ContentSearchVo contentSearchVo : contentSearchList){
			searchResultVo = new SearchResultVo();
			searchResultVo.setId(contentSearchVo.getId());
			searchResultVo.setHtmlTitle(StringUtils.defaultIfBlank(contentSearchVo.getHtmlTitle(), "") );
			searchResultVo.setSourceTitle(StringUtils.defaultIfBlank(contentSearchVo.getTitle(), ""));
			searchResultVo.setContent(StringUtils.defaultIfBlank(contentSearchVo.getIntroduction(), ""));
			searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_CONTENT);
			searchResultVo.setShopId(StringUtils.defaultIfBlank(contentSearchVo.getShopId(), ""));
			resultVos.add(searchResultVo);
		}

		return resultVos;
	}

	@Override
	public List<SearchResultVo> findActivityFromEsByTitle(String title, PagingVo pagingVo) {
		List<ActivitySearchVo> activitySearchList = this.findActivitySearchFromEsByTitle(title, pagingVo);
		List<SearchResultVo> searchResultList = new ArrayList<>();
		if(CollectionUtils.isEmpty(activitySearchList)){
			return searchResultList;
		}

		SearchResultVo searchResultVo;
		for(ActivitySearchVo activitySearchVo : activitySearchList){
			searchResultVo = new SearchResultVo();
			searchResultVo.setId(activitySearchVo.getId());
			searchResultVo.setShopId(activitySearchVo.getOrgnizerId());
			searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_ACTIVITY);
			searchResultVo.setHtmlTitle(activitySearchVo.getHtmlTitle());
			searchResultVo.setSourceTitle(activitySearchVo.getTitle());
			searchResultVo.setContent(activitySearchVo.getContent());
			searchResultList.add(searchResultVo);
		}

		return searchResultList;
	}






	@Override
	public List<ActivitySearchVo> findActivitySearchFromEsByTitle(String title, PagingVo pagingVo){
		List<ActivitySearchVo> resultVos = new ArrayList<>();
		if(StringUtils.isBlank(title)){
			return resultVos;
		}
		Pageable pageable = new PageRequest(pagingVo.getPn(), pagingVo.getPs());
		QueryStringQueryBuilder builder=new QueryStringQueryBuilder(title).defaultField("title").analyzer("ik_smart");

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(builder)
				.withIndices(indexNameProperties.getSitesIndexName())
				.withTypes("activity")
				.withHighlightFields(new HighlightBuilder.Field("title").fragmentSize(500).numOfFragments(20).preTags("<font color = red>").postTags("</font>"))
				.withPageable(pageable)
				.build();

		template.queryForPage(searchQuery, ActivitySearchVo.class, new SearchResultMapper() {
			@Override
			public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
				SearchHit[] searchHits = response.getHits().getHits();
				if(searchHits==null || searchHits.length<1){
					return null;
				}

				for (SearchHit searchHit : response.getHits()) {
					String source = searchHit.getSourceAsString();
					ActivitySearchVo activitySearchVo = null;
					try {
						activitySearchVo = MAPPER.readValue(source, ActivitySearchVo.class);
						activitySearchVo.setId(searchHit.getId());
						activitySearchVo.setHtmlTitle(searchHit.getHighlightFields().get("title").fragments()[0].toString());
						resultVos.add(activitySearchVo);
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}
				}
				return null;
			}
		});

		return resultVos;
	}


	@Override
	public List<SearchResultVo> findTicketFromEsByName(String name, PagingVo pagingVo) {
		List<TicketSearchVo> ticketSearchList = this.findTicketSearchFromEsByName(name, pagingVo);
		List<SearchResultVo> searchResultList = new ArrayList<>();
		if(CollectionUtils.isEmpty(ticketSearchList)){
			return searchResultList;
		}

		SearchResultVo searchResultVo;
		for(TicketSearchVo ticketSearchVo : ticketSearchList){
			searchResultVo = new SearchResultVo();
			//searchResultVo.setZoneCity(searchResultVo.getZoneCity());
			searchResultVo.setSourceTitle(ticketSearchVo.getSpuName());
			searchResultVo.setHtmlTitle(ticketSearchVo.getHtmlSpuName());
			searchResultVo.setShopId(ticketSearchVo.getShopId());
			searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_TICKET);
			searchResultVo.setContent(ticketSearchVo.getSpecifications());
			searchResultList.add(searchResultVo);
		}

		return searchResultList;
	}

	@Override
	public List<TicketSearchVo> findTicketSearchFromEsByName(String name, PagingVo pagingVo) {
		List<TicketSearchVo> ticketSearchList = new ArrayList<>();
		if(StringUtils.isBlank(name)){
			return ticketSearchList;
		}

		BoolQueryBuilder spuBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("catalogCode", "0"))
				.must(QueryBuilders.termQuery("saleState", 1));

		SearchQuery spuSearchQuery = new NativeSearchQueryBuilder()
				.withQuery(spuBuilder)
				.withIndices(indexNameProperties.getSitesIndexName())
				.withTypes("spu_collection")
				.build();

		List<String> spuCodeList = template.query(spuSearchQuery, new ResultsExtractor<List<String>>() {
			@Override
			public List<String> extract(SearchResponse searchResponse) {

				SearchHit[] searchHits = searchResponse.getHits().getHits();
				List<String> resultList = new ArrayList<>();
				if(searchHits==null || searchHits.length<1){
					return resultList;
				}

				for (SearchHit searchHit : searchResponse.getHits()) {
					String source = searchHit.getSourceAsString();
					try {
						HashMap<String, Object> map = MAPPER.readValue(source, HashMap.class);
						String spuCode = "";
						if(map.get("spuCode")!=null && StringUtils.isNotBlank((spuCode=map.get("spuCode").toString()))){
							resultList.add(spuCode);
						}
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}
				}
				return resultList;
			}
		});

		if(CollectionUtils.isEmpty(spuCodeList)){
			return ticketSearchList;
		}
		Pageable pageable = new PageRequest(pagingVo.getPn(), pagingVo.getPs());

		BoolQueryBuilder builder =  QueryBuilders.boolQuery().must(QueryBuilders.termsQuery("spuCode", spuCodeList))
				.must(QueryBuilders.termQuery("saleState", 1));
//				.must(QueryBuilders.queryStringQuery(name).defaultField("spuName").analyzer("ik_smart"));

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(builder)
				.withIndices(indexNameProperties.getSitesIndexName())
				.withTypes("sku_collection")
				.withHighlightFields(new HighlightBuilder.Field("spuName").fragmentSize(500).numOfFragments(20).preTags("<font color = red>").postTags("</font>"))
				.withPageable(pageable)
				.build();

		template.queryForPage(searchQuery, TicketSearchVo.class, new SearchResultMapper() {
			@Override
			public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
				SearchHit[] searchHits = response.getHits().getHits();

				if(searchHits==null || searchHits.length<1){
					return null;
				}

				for (SearchHit searchHit : response.getHits()) {
					String source = searchHit.getSourceAsString();
					TicketSearchVo ticketSearchVo = null;
					try {
						ticketSearchVo = MAPPER.readValue(source, TicketSearchVo.class);
						ticketSearchVo.setId(searchHit.getId());
						ticketSearchVo.setHtmlSpuName(searchHit.getHighlightFields().get("spuName").fragments()[0].toString());
						ticketSearchVo.setDiscountPrice(ticketSearchVo.getDiscountPrice()/100.0);
						ticketSearchVo.setPrice(ticketSearchVo.getPrice()/100.0);
						ticketSearchList.add(ticketSearchVo);
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}
				}

				return null;
			}
		});

		return ticketSearchList;
	}

	@Override
	public List<SitesSearchVo> findSitesSearchFromEsByTitle(String title, PagingVo pagingVo){
		List<SitesSearchVo> sitesSearchList = new ArrayList<>();
		if(StringUtils.isBlank(title)){
			return sitesSearchList;
		}

		Pageable pageable = new PageRequest(pagingVo.getPn(), pagingVo.getPs());
		QueryBuilder queryBuilder = QueryBuilders.boolQuery()
				.must(QueryBuilders.queryStringQuery(title).defaultField("name").analyzer("ik_smart"))
				.must(QueryBuilders.termsQuery("status", ConstantUtil.SITES_STATUS_COLLECT, ConstantUtil.SITES_STATUS_ONLINE))
				.must(QueryBuilders.termQuery("delFlag", ConstantUtil.NOT_DELETE_STATUS));

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(queryBuilder)
				.withIndices(indexNameProperties.getSitesIndexName())
				.withTypes("sites")
				.withHighlightFields(new HighlightBuilder.Field("name").fragmentSize(500).numOfFragments(20).preTags("<font color = red>").postTags("</font>"))
				.withPageable(pageable)
				.build();

		template.queryForPage(searchQuery, SearchResultVo.class, new SearchResultMapper() {
			@Override
			public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

				SearchHit[] searchHits = response.getHits().getHits();
				if(searchHits==null || searchHits.length<1){
					return null;
				}

				for (SearchHit searchHit : response.getHits()) {
					String source = searchHit.getSourceAsString();
					SitesSearchVo sitesSearchVo = null;
					try {
						sitesSearchVo = MAPPER.readValue(source, SitesSearchVo.class);
						sitesSearchVo.setId(searchHit.getId());
						sitesSearchVo.setHtmlName(searchHit.getHighlightFields().get("name").fragments()[0].toString());
						sitesSearchList.add(sitesSearchVo);
					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}
				}

				return null;
			}
		});

		return sitesSearchList;
	}

	@Override
	public List<SearchResultVo> findSitesFromEsByTitle(String name, PagingVo pagingVo) {

		List<SitesSearchVo> sitesSearchList = this.findSitesSearchFromEsByTitle(name, pagingVo);
		List<SearchResultVo> searchResultList = new ArrayList<>();
		if(CollectionUtils.isEmpty(sitesSearchList)){
			return searchResultList;
		}

		SearchResultVo searchResultVo;
		for(SitesSearchVo sitesSearchVo : sitesSearchList){
			searchResultVo = new SearchResultVo();
			searchResultVo.setId(sitesSearchVo.getId());
			searchResultVo.setHtmlTitle(sitesSearchVo.getHtmlName());
			searchResultVo.setSourceTitle(sitesSearchVo.getName());
			searchResultVo.setContent(sitesSearchVo.getIntroduction());
			searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_SITES);
			searchResultVo.setShopId(sitesSearchVo.getId());
			searchResultVo.setZoneCity(sitesSearchVo.getZoneCity());
			searchResultVo.setLatitude(sitesSearchVo.getCenterLatitude());
			searchResultVo.setLongitude(sitesSearchVo.getCenterLongitude());
			searchResultList.add(searchResultVo);
		}

		return searchResultList;
	}

	@Override
	public List<SearchResultVo> findSitesFromFuzzyByTitle(String name, PagingVo pagingVo) {
		List<SearchResultVo> resultVos = new ArrayList<>();
		if (StringUtils.isBlank(name)) {
			return resultVos;
		}

		List<SitesEntity> sitesEntityList = sitesRepo.fuzzyBySitesAddr(name,pagingVo);

		if(null == sitesEntityList || sitesEntityList.size()<1){
			return resultVos;
		}

		try {
			for (int i = 0; i < sitesEntityList.size(); i++) {
				SearchResultVo searchResultVo = new SearchResultVo();
				searchResultVo.setId(sitesEntityList.get(i).getId());
				searchResultVo.setHtmlTitle(sitesEntityList.get(i).getAddress());
				searchResultVo.setSourceTitle(sitesEntityList.get(i).getName());
				searchResultVo.setContent(sitesEntityList.get(i).getShortName());
				searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_DEST);
				searchResultVo.setZoneCity(sitesEntityList.get(i).getZoneCity());
				searchResultVo.setLongitude(sitesEntityList.get(i).getCenterLongitude());
				searchResultVo.setLatitude(sitesEntityList.get(i).getCenterLatitude());
				resultVos.add(searchResultVo);

			}
			return resultVos;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}
		return resultVos;

	}




	@Override
	public List<SearchResultVo> findZoneMasterFromEsByName(String name, PagingVo pagingVo) {
		List<SearchResultVo> resultVos = new ArrayList<>();
		if(StringUtils.isBlank(name)){
			return resultVos;
		}
		try {
			SearchRequestBuilder searchRequestBuilder = client
					.prepareSearch(indexNameProperties.getSitesIndexName());
			searchRequestBuilder.setTypes("zone_master");
			searchRequestBuilder.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);

//			QueryBuilder queryBuilder = QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", name)
//					.fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10).boost(0.98f))
//					.should(QueryBuilders.wildcardQuery("name", "*" + name + "*").boost(0.8f))
//					.must(QueryBuilders.termQuery("levels", 1));
			
			int [] levelss = {1,2};
			QueryBuilder queryBuilder = QueryBuilders.boolQuery()
					.must(QueryBuilders.termsQuery("levels", levelss))
					.must(QueryBuilders.queryStringQuery(name).defaultField("name").analyzer("ik_smart"));

			//queryBuilder = QueryBuilders.prefixQuery("title", title);
			// 设置查询关键词
			searchRequestBuilder.setQuery(queryBuilder);
			// 分页应用
			searchRequestBuilder.setFrom(pagingVo.getPn()).setSize(pagingVo.getPs());

			// 设置是否按查询匹配度排序
			searchRequestBuilder.setExplain(true);

			//设置高亮显示
			searchRequestBuilder.addHighlightedField("name");
			searchRequestBuilder.setHighlighterPreTags("<font color = red>");
			searchRequestBuilder.setHighlighterPostTags("</font>");
			// 执行搜索,返回搜索响应信息
			SearchResponse response = searchRequestBuilder.execute().actionGet();

			SearchHits searchHits = response.getHits();
			SearchHit[] hits = searchHits.getHits();
			for (int i = 0; i < hits.length; i++) {
				SearchResultVo searchResultVo = new SearchResultVo();
				SearchHit hit = hits[i];
				String json = hit.getSourceAsString();

				JSONObject jsonObject = JSON.parseObject(json);

				Map<String, HighlightField> result = hit.highlightFields();
				HighlightField titleField = result.get("name");
				Text[] titleTexts = titleField.fragments();
				String title = "";
				for (Text text : titleTexts) {
					title += text;
				}

				searchResultVo.setId(hit.getId());
				searchResultVo.setHtmlTitle(title);
				searchResultVo.setSourceTitle(jsonObject.getString("name"));
				searchResultVo.setContent(jsonObject.getString("shortName"));
				searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_DEST);
				searchResultVo.setZoneCity(jsonObject.getString("code"));
				searchResultVo.setLongitude(jsonObject.getDouble("longitude"));
				searchResultVo.setLatitude(jsonObject.getDouble("latitude"));
				resultVos.add(searchResultVo);
			}

			return resultVos;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return resultVos;
	}

	@Override
	public List<SearchResultVo> findZoneMasterFromFuzzyByName(String name,String cityCode, PagingVo pagingVo) {
		List<SearchResultVo> resultVos = new ArrayList<>();
		if (StringUtils.isBlank(name)) {
			return resultVos;
		}

		List<SitesEntity> sitesEntityList = sitesRepo.fuzzyBySitesName(name,cityCode,pagingVo);

		if(null == sitesEntityList || sitesEntityList.size()<1){
			return resultVos;
		}

		try {
			for (int i = 0; i < sitesEntityList.size(); i++) {
				SearchResultVo searchResultVo = new SearchResultVo();
				searchResultVo.setId(sitesEntityList.get(i).getId());
				searchResultVo.setHtmlTitle(sitesEntityList.get(i).getName());
				searchResultVo.setSourceTitle(sitesEntityList.get(i).getName());
				searchResultVo.setContent(sitesEntityList.get(i).getShortName());
				searchResultVo.setType(ConstantUtil.SEARCH_RESULT_TYPE_SITES);
				searchResultVo.setZoneCity(sitesEntityList.get(i).getZoneCity());
				searchResultVo.setLongitude(sitesEntityList.get(i).getCenterLongitude());
				searchResultVo.setLatitude(sitesEntityList.get(i).getCenterLatitude());
				resultVos.add(searchResultVo);

			}
			return resultVos;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}
			return resultVos;
	}


	@Override
	public List<SitesPointEntity> findSitesPointFromEsByName(String name, PagingVo pagingVo) {
		List<SitesPointEntity> resultVos = new ArrayList<>();
		if(StringUtils.isBlank(name)){
			return resultVos;
		}

		SearchRequestBuilder searchRequestBuilder = client
				.prepareSearch(indexNameProperties.getSitesIndexName());
		searchRequestBuilder.setTypes("sites_point");
		searchRequestBuilder.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);

		QueryBuilder queryBuilder = QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", name)
				.fuzziness(Fuzziness.AUTO).prefixLength(3).maxExpansions(10).boost(0.98f))
				.should(QueryBuilders.wildcardQuery("detailInfo", "*" + name + "*").boost(0.8f));

		queryBuilder = QueryBuilders.queryStringQuery(name).defaultField("name");

		searchRequestBuilder.setQuery(queryBuilder);
		searchRequestBuilder.setFrom(pagingVo.getPageOffset()).setSize(pagingVo.getPs());
		searchRequestBuilder.setExplain(false);
		SearchResponse response = searchRequestBuilder.execute().actionGet();

		SearchHits searchHits = response.getHits();
		SearchHit[] hits = searchHits.getHits();
		for (int i = 0; i < hits.length; i++) {
			SearchHit hit = hits[i];
			String json = hit.getSourceAsString();

			SitesPointEntity sitesPointEntity;
			try {
				sitesPointEntity = JSONObject.parseObject(json, SitesPointEntity.class);
				sitesPointEntity.setId(hit.getId());
				resultVos.add(sitesPointEntity);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}

		return resultVos;
	}

	@Override
	public List<SearchResultVo> findSitesPointResultFromEsByName(String name, PagingVo pagingVo) {
		//反序列化时，可将一个String转为List<String>
		MAPPER.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

		List<SearchResultVo> searchResultList = new ArrayList<>();
		Pageable pageable = new PageRequest(pagingVo.getPn(), pagingVo.getPs());
		QueryBuilder queryBuilder = QueryBuilders.boolQuery()
				.must(QueryBuilders.queryStringQuery(name).defaultField("name").analyzer("ik_smart"));

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(queryBuilder)
				.withIndices(indexNameProperties.getSitesIndexName())
				.withTypes("sites_point")
				.withHighlightFields(new HighlightBuilder.Field("name").fragmentSize(500).numOfFragments(20).preTags("<font color = red>").postTags("</font>"))
				.withPageable(pageable)
				.build();


		template.queryForPage(searchQuery, SitesPointEntity.class, new SearchResultMapper() {
			@Override
			public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

				SearchHit[] searchHits = response.getHits().getHits();
				if(searchHits==null || searchHits.length<1){
					return null;
				}

				SearchResultVo searchResultVo;
				for (SearchHit searchHit : response.getHits()) {
					String source = searchHit.getSourceAsString();
					SitesPointEntity sitesPointEntity;
					try {
						sitesPointEntity = MAPPER.readValue(source, SitesPointEntity.class);
						searchResultVo = new SearchResultVo();

						searchResultVo.setLatitude(sitesPointEntity.getLatitude());
						searchResultVo.setLongitude(sitesPointEntity.getLongitude());
						searchResultVo.setId(searchHit.getId());
						searchResultVo.setShopId(sitesPointEntity.getSitesId());
						searchResultVo.setType(60);
						searchResultVo.setHtmlTitle(searchHit.getHighlightFields().get("name").fragments()[0].toString());
						searchResultVo.setContent(sitesPointEntity.getName());
						searchResultVo.setSourceTitle(sitesPointEntity.getName());

						searchResultList.add(searchResultVo);

					}catch (Exception e){
						logger.error(e.getMessage(), e);
					}
				}

				return null;
			}
		});
		return searchResultList;
	}

	@Override
	public long count(String zoneCity,String zoneDistrict,String sitesStatus) {
		return sitesRepo.count(zoneCity, zoneDistrict,sitesStatus);
	}

	@Override
	public List<SitesEntity> getAllSites() {
		return sitesRepo.getAllSites();
	}


	@Override
	public SitesPointEntity findSitesPointByPointId(String pointId) {
		if(StringUtils.isBlank(pointId)){
			return null;
		}
		return sitesPointRepo.findById(pointId);
	}

	@Override
	public List<SitesPointEntity> findByPointNameVag(String pointName,String sitesId) {
		return sitesPointRepo.findByPointNameVag(pointName,sitesId);
	}

	@Override
	public SitesPointEntity findByPointName(String pointName) {
		return sitesPointRepo.findByPointName(pointName);
	}

	@Override
	public List<SitesPointEntity> findMPlayPointBySitesId(String sitesId) {
		return sitesPointRepo.findMPlayPointBySitesId(sitesId);
	}

	@Override
	public List<SitesRoutePointEntity> findSitesRoutePointByRouteId(String routeId) {
		return sitesRoutePointRepo.findSitesRoutePointByRouteId(routeId);
	}

	@Override
	public List<SitesEntity> findByName(String name) {
		
		return sitesRepo.findByName(name);
	}

	@Override
	public List<SitesPointEntity> getSitesPointByPoint(double latitude, double longitude, long nearM, PagingVo pagingVo) {
		return sitesPointRepo.getSitesPointByPoint(latitude, longitude, nearM, pagingVo);
	}

	@Override
	public List<SitesEntity> getSitesByPoint(double latitude, double longitude, long nearM, PagingVo pagingVo) {
		return sitesRepo.getSitesByPoint(latitude, longitude, nearM, pagingVo);
	}

	@Override
	public boolean findPointName(String pointName, String sitesId) {
		return false;
	}

	@Override
	public SitesEntity findSitesByMerchantId(String merchantId) {
		return sitesRepo.findSitesByMerchantId(merchantId);
	}

	@Override
	public List<SitesEntity> findSitesList(String k, String cityCode, PagingVo pagingVo) {
		return sitesRepo.findSitesList(k,cityCode,pagingVo);
	}
}
